package Thread;

// 单例模式,只能创建一个实例

// 饿汉模式，程序启动，立即创建实例
class Singleton {
    private static Singleton instance = new Singleton(); // 当 Singleton类被加载到这里，就会执行到这里的实例化操作

    public static Singleton getInstance() {
        return instance;
    }

    // 构造方法设为私有，其他的类想来 new 就不行
    private Singleton() {

    }
}

// 懒汉模式，程序启动，等到要用的时候再创建实例
class SingletonLazy {
    // 保证内存可见性
    //
    volatile private static SingletonLazy instance = null; // 此处没有创建实例

    public static SingletonLazy getInstance() { // 首次调用 getInstance才会创建实例
        // 只有在初始情况下才会出现线程不安全，既然如此后续的 getInstance()就不在需简要加锁（线程安全后加锁非常影响效率）
        if (instance == null) { // 如果初始化好直接返回 instance。未初始化线程进行加锁：1、有三个线程, 开始执行 getInstance , 通过外层的 if (instance == null) 知道了实例还没有创建的消息. 于是开始竞争同一把锁
            // 把这两个步骤打包在一起，保证 读、判定、修改 这几个操作是原子性的
            // 不然会出现一个线程正在读，发现为空，准备进行写；
            // 但是另一个线程也去读发现为空，出现了两个线程一起写
            synchronized (SingletonLazy.class) {
                /**
                 * 2、其中线程1 率先获取到锁, 此时线程1 通过里层的 if (instance == null) 进一步确认实例是
                 *  否已经创建. 如果没创建, 就把这个实例创建出来.
                 * 3、当线程1 释放锁之后, 线程2 和 线程3 也拿到锁, 也通过里层的 if (instance == null) 来
                 *  确认实例是否已经创建, 发现实例已经创建出来了, 就不再创建了.
                 */
                if (instance == null) { // 读  // 如果多线程尝试初始化，产生锁竞争；
                    instance = new SingletonLazy(); // 写,线程不安全
                }
            }
        }


        return instance;
    }

    private SingletonLazy() {};
}

public class Demo18 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true

        //Singleton instance3 = new Singleton(); // 类内部可用，类外部不可用，保证实例只有一个(唯一性)


    }
}
